1 /*
2 * Scope: a generic MVC framework.
3 * Copyright (c) 2000-2002, The Scope team
4 * All rights reserved.
5 *
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * Neither the name "Scope" nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 *
36 * $Id: XSLPage.java,v 1.7 2002/09/05 15:41:45 ludovicc Exp $
37 */
38 package org.scopemvc.view.servlet.xml;
39
40
41 import java.util.HashMap;
42 import java.util.Iterator;
43 import java.util.LinkedList;
44 import java.util.List;
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47 import org.scopemvc.core.PropertyManager;
48 import org.scopemvc.core.Selector;
49 import org.scopemvc.util.Debug;
50 import org.scopemvc.util.ScopeConfig;
51 import org.scopemvc.util.convertor.StringConvertor;
52 import org.scopemvc.util.convertor.StringConvertors;
53 import org.xml.sax.ContentHandler;
54 import org.scopemvc.view.servlet.ValidationFailure;
55
56 /***
57 * <P>
58 *
59 * A concrete {@link AbstractXSLPage} that uses Scope's ModelManager
60 * implementations to serialise its entire bound model object to an XML
61 * document. A better strategy would be to use a more intelligent view that
62 * selectively serialises relevant parts of the model object. </P> <P>
63 *
64 * Handles circular references using the "ID" and "IDREF" pattern. </P>
65 *
66 * @author <A HREF="mailto:smeyfroi@users.sourceforge.net">Steve Meyfroidt</A>
67 * @created 05 September 2002
68 * @version $Revision: 1.7 $ $Date: 2002/09/05 15:41:45 $
69 */
70 public class XSLPage extends AbstractXSLPage {
71
72 /***
73 * The prefix on form parameters to identify a property/value pair used to
74 * repopulate the View's bound model. A property/value pair looks like:
75 * <CODE>
76 * <PROPERTY_ID_PREFIX>ViewID<VIEW_ID_SUFFIX>SelectorDescription
77 * </CODE>
78 *
79 * @see #populateModel
80 */
81 static char PROPERTY_ID_PREFIX;
82
83 private final static Log LOG = LogFactory.getLog(XSLPage.class);
84
85 /***
86 * Optional setting for whether the SAX convertor should write all the
87 * property "paths" out when this view is streamed. Some views might want
88 * this if they send back form parameters like <CODE>{path}=newValue</CODE>
89 * for automatic repopulation back into the bound model object.
90 */
91 protected boolean requiresPropertyDescriptions;
92
93 /***
94 * TODO: describe of the Field
95 */
96 protected ModelToXML xmlGenerator = new ModelToXML();
97
98
99 /***
100 * @param inViewID unique View ID for routing incoming Controls
101 * @param inXslURI the XSLT this View uses to transform its model objects
102 * after they convert to XML
103 */
104 public XSLPage(String inViewID, String inXslURI) {
105 this(inViewID, inXslURI, false);
106 }
107
108
109 /***
110 * @param inViewID unique View ID for routing incoming Controls
111 * @param inXslURI the XSLT this View uses to transform its model objects
112 * after they convert to XML
113 * @param inRequiresModelIds Does this view need the SAX convertor to write
114 * out property description attributes for properties? For repopulation
115 * back into the bound model via {@link #populateModel}
116 */
117 public XSLPage(String inViewID, String inXslURI, boolean inRequiresModelIds) {
118 super(inViewID, inXslURI);
119 requiresPropertyDescriptions = inRequiresModelIds;
120 init();
121 }
122
123
124 /***
125 * <P>
126 *
127 * Interprets any form parameters like <CODE>
128 * <PROPERTY_ID_PREFIX>SelectorDescription
129 * </CODE> as [property_description, property_value] pairs </P> <P>
130 *
131 * Extracts the property_description from the form parameter key, then
132 * populates its model object using the String value. Any parameters treated
133 * this way are removed from the HashMap. </P>
134 *
135 * @param ioParameters form parameters to parse for [property_description,
136 * property_value] pairs, removing any processed pairs from the
137 * parameters before return
138 * @return PopulateModelFailedException that will be handled as a validation
139 * failure.
140 */
141 public List populateModel(HashMap ioParameters) {
142 if (LOG.isDebugEnabled()) {
143 LOG.debug("populateModel: " + ioParameters);
144 }
145
146 List errors = new LinkedList();
147 // collect ValidationFailures in here
148 List toRemove = new LinkedList();
149 for (Iterator i = ioParameters.keySet().iterator(); i.hasNext(); ) {
150 Object o = i.next();
151 if (Debug.ON) {
152 Debug.assertTrue(o instanceof String);
153 }
154 String parameterKey = (String) o;
155 if (LOG.isDebugEnabled()) {
156 LOG.debug("populateModel: " + parameterKey);
157 }
158
159 // Recognise prefixed form parameters for model population
160 if (parameterKey.charAt(0) == PROPERTY_ID_PREFIX) {
161
162 // get the form parameter value and remove from the parameter list
163 o = ioParameters.get(parameterKey);
164 // ... if multiple values take the first
165 if (o instanceof Object[]) {
166 if (Debug.ON) {
167 Debug.assertTrue(((Object[]) o).length > 0);
168 }
169 o = ((Object[]) o)[0];
170 }
171 if (Debug.ON) {
172 Debug.assertTrue(o instanceof String);
173 }
174 String stringValue = (String) o;
175
176 // Mark it for removal (avoid concurrency problem with Iterator)
177 toRemove.add(o);
178
179 // find the property description
180 String propertyDescription = parameterKey.substring(1);
181 try {
182 populateBoundModelProperty(propertyDescription, stringValue);
183 } catch (Exception e) {
184 if (LOG.isDebugEnabled()) {
185 LOG.debug("populateModel: got an exception: " + e);
186 }
187 errors.add(new ValidationFailure(propertyDescription, stringValue, e));
188 }
189 }
190 }
191
192 for (Iterator i = toRemove.iterator(); i.hasNext(); ) {
193 ioParameters.remove(i.next());
194 }
195
196 if (errors.size() == 0) {
197 errors = null;
198 }
199 return errors;
200 }
201
202
203 /***
204 * Do it like this so that we can pick up application-specific
205 * ScopeConfig... static initializers would happen before user got a chance
206 * to setup the custom config properties.
207 */
208 protected void init() {
209 PROPERTY_ID_PREFIX = ScopeConfig.getChar("ServletFormParameter.propertyIDPrefix");
210 if (PROPERTY_ID_PREFIX == 0) {
211 LOG.fatal("No propertyIDPrefix in config.");
212 }
213 }
214
215
216 /***
217 * @param inContentHandler Drive this ContentHandler with the Model's SAX
218 * events.
219 * @throws Exception TODO: Describe the Exception
220 */
221 protected void generateXMLDocument(ContentHandler inContentHandler)
222 throws Exception {
223 if (requiresPropertyDescriptions) {
224 xmlGenerator.modelToXML(getBoundModel(), inContentHandler,
225 new FullIDGenerator());
226 } else {
227 xmlGenerator.modelToXML(getBoundModel(), inContentHandler,
228 new NoIDGenerator());
229 }
230 }
231
232
233 /***
234 * Use the property_description, property_value pair passed to set a
235 * property in the bound model to a new value. Use StringConvertor if
236 * available to convert from String to the property's native datatype.
237 *
238 * @param inPropertyDescription TODO: Describe the Parameter
239 * @param inValue TODO: Describe the Parameter
240 * @throws Exception on any failure
241 */
242 protected void populateBoundModelProperty(String inPropertyDescription, String inValue)
243 throws Exception {
244 if (LOG.isDebugEnabled()) {
245 LOG.debug("populateBoundModelProperty: " + inPropertyDescription + ", " + inValue);
246 }
247
248 if (getBoundModel() == null) {
249 LOG.error("No bound model for: " + this);
250 return;
251 }
252
253 // Get the PropertyManager for the bound model
254 Object model = getBoundModel();
255 PropertyManager manager = PropertyManager.getInstance(model);
256 if (Debug.ON) {
257 Debug.assertTrue(manager != null, "null manager");
258 }
259
260 // Get the Selector from the property description
261 Selector selector = Selector.fromString(inPropertyDescription);
262
263 // Use StringConvertors if available, else just set(Selector, String)
264 StringConvertor convertor = StringConvertors.forClass(manager.getPropertyClass(model, selector));
265 if (convertor != null) {
266 if (LOG.isDebugEnabled()) {
267 LOG.debug("populateProperty: got " + convertor.getClass());
268 }
269 Object value = convertor.stringAsValue(inValue);
270 manager.set(model, selector, value);
271 } else {
272 if (LOG.isDebugEnabled()) {
273 LOG.debug("populateProperty: no StringConvertor ");
274 }
275 manager.set(model, selector, inValue);
276 }
277 }
278 }
279
280 final class NoIDGenerator extends PropertyIDGenerator {
281
282 /***
283 * Gets the property ID
284 *
285 * @return The propertyID value
286 */
287 public String getPropertyID() {
288 return null;
289 }
290 }
291
292 final class FullIDGenerator extends PropertyIDGenerator {
293
294 /***
295 * Gets the property ID
296 *
297 * @return The propertyID value
298 */
299 public String getPropertyID() {
300 if (currentPropertySelector == null) {
301 return null;
302 }
303 return XSLPage.PROPERTY_ID_PREFIX
304 + Selector.asString(currentPropertySelector);
305 }
306 }
This page was automatically generated by Maven